package com.zige.zige.view.panoplayer;

import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.support.annotation.NonNull;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.Surface;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.zige.zige.view.panoplayer.glwrapper.GlCamera;

/**
 * Created by admin on 16/1/5.
 */
public class PanoPlayerView extends GLSurfaceView implements GLSurfaceView.Renderer {

    private final float MAX_FOVY = 130.0f;
    private final float MIN_FOVY = 60.0f;

    static final float PI_2 = (float) (Math.PI / 2.0);
    static final float _2_PI = (float) (2.0 * Math.PI);
    protected AbstractVideoPlayer videoPlayer;
    private AbstractPanoRenderer panoRenderer;
    private MotionTracker motionTracker;
    private GlCamera camera;
    private boolean vrModeEnabled = false;
    private boolean gyroControlEnabled = false;
    private float touchControlAngleOnX = 0.0f;
    private float touchControlAngleOnY = 0.0f;
    private float touchLastX = 0.0f;
    private float touchLastY = 0.0f;
    private ScaleGestureDetector mScaleGestureDetector;
    private boolean pinching = false;

    public PanoPlayerView(Context context) {
        super(context);
        init();
    }

    public PanoPlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        videoPlayer = new VideoPlayer(getContext());
        panoRenderer = new PanoRenderer(getContext());
        motionTracker = MotionTracker.createFromContext(getContext());
        camera = new GlCamera();

        mScaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float fovy = camera.getFovy() / detector.getScaleFactor();
                if (fovy < MIN_FOVY) fovy = MIN_FOVY;
                else if (fovy > MAX_FOVY) fovy = MAX_FOVY;
                camera.setFovy(fovy);
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                pinching = true;
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
                touchLastX = detector.getFocusX();
                touchLastY = detector.getFocusY();
            }
        });
        this.setEGLContextClientVersion(2);
        this.setPreserveEGLContextOnPause(true);
        setRenderer(this);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        panoRenderer.onSurfaceCreated();
        videoPlayer.setSurface(new Surface(panoRenderer.getSurfaceTexture()));
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        camera.setAspect((width + 0.0f) / height);
        panoRenderer.onSurfaceChanged(width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        if (gyroControlEnabled) {
            float[] view = new float[16];
            motionTracker.getLastHeadView(view, 0);
            camera.setViewMatrix(view);
        } else {
            synchronized (this) {
                camera.resetView();
                float[] rotationX = new float[16];
                float[] rotationY = new float[16];
                Matrix.setRotateM(rotationX, 0, (float) Math.toDegrees(touchControlAngleOnX), 1.0f, 0.0f, 0.0f);
                Matrix.setRotateM(rotationY, 0, (float) Math.toDegrees(touchControlAngleOnY), 0.0f, 1.0f, 0.0f);
                float[] rotation = new float[16];
                Matrix.multiplyMM(rotation, 0, rotationX, 0, rotationY, 0);
                camera.rotate(rotation);
            }
        }

        panoRenderer.prepareFrame();

        int width = this.getWidth();
        int height = this.getHeight();

        if (vrModeEnabled) {
            GLES30.glEnable(GLES30.GL_SCISSOR_TEST);
            GLES30.glViewport(0, 0, width, height / 2);
            GLES30.glScissor(0, 0, width, height / 2);
            panoRenderer.onDrawFrame(camera);
            GLES30.glViewport(0, height - height / 2, width, height / 2);
            GLES30.glScissor(0, height - height / 2, width, height / 2);
            panoRenderer.onDrawFrame(camera);
        } else {
            panoRenderer.onDrawFrame(camera);
        }

        GLES30.glViewport(0, 0, width, height);
        GLES30.glScissor(0, 0, width, height);
    }

    private boolean isMoving = false;

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        mScaleGestureDetector.onTouchEvent(event);

        int action = MotionEventCompat.getActionMasked(event);
        switch (action) {
            case MotionEvent.ACTION_UP:
                if (!isMoving) {
                    titleAndBottomListener.showOrhide();
                }
                break;
            default:
                break;
        }
        if (event.getPointerCount() != 1 || gyroControlEnabled) return true;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                touchLastX = event.getX();
                touchLastY = event.getY();
                isMoving = false;
                return true;
            case MotionEvent.ACTION_UP:
                pinching = false;
                return true;
            case MotionEvent.ACTION_MOVE:
                if (pinching) return true;
                float scale = -0.00001f * camera.getFovy();
                float deltaX = (event.getX() - touchLastX) * scale;
                float deltaY = (event.getY() - touchLastY) * scale;
                touchLastX = event.getX();
                touchLastY = event.getY();
                synchronized (this) {
                    this.touchControlAngleOnX += deltaY;
                    this.touchControlAngleOnY += deltaX;
                    if (touchControlAngleOnX > PI_2) touchControlAngleOnX = PI_2;
                    else if (touchControlAngleOnX < -PI_2) touchControlAngleOnX = -PI_2;
                    if (touchControlAngleOnY > _2_PI) touchControlAngleOnY -= _2_PI;
                    else if (touchControlAngleOnY < 0) touchControlAngleOnY += _2_PI;
                }

                if (Math.abs(deltaX) > 0 || Math.abs(deltaY) > 0) {
                    isMoving = true;
                }
                return true;
            default:
                return super.onTouchEvent(event);
        }
    }

    public void setGyroControlEnabled(boolean gyroControlEnabled) {
        if (!(this.gyroControlEnabled ^ gyroControlEnabled)) return;
        if (gyroControlEnabled) {
            motionTracker.startTracking();
        } else {
            motionTracker.stopTracking();
        }
        this.gyroControlEnabled = gyroControlEnabled;
    }

    public void setVrModeEnabled(boolean vrModeEnabled) {
        float width = this.getWidth();
        float height = this.getHeight();

        float aspect;
        if (vrModeEnabled) {
            aspect = width / height;
            aspect *= 2;
            camera.setAspect(aspect);
        } else {
            aspect = width / height * 2;
            aspect *= 0.5;
            camera.setAspect(aspect);
        }
//        camera.setAspect(aspect);
        this.vrModeEnabled = vrModeEnabled;
    }

    public boolean isGyroControlEnabled() {
        return gyroControlEnabled;
    }

    public boolean isVrModeEnabled() {
        return vrModeEnabled;
    }

    public AbstractVideoPlayer getVideoPlayer() {
        return videoPlayer;
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (videoPlayer != null) {
            videoPlayer.cleanUp();
        }
        panoRenderer.cleanUp();
        setGyroControlEnabled(false);
    }

    public void setTitleAndBottomListener(TitleAndBottomListener titleAndBottomListener) {
        this.titleAndBottomListener = titleAndBottomListener;
    }

    private TitleAndBottomListener titleAndBottomListener;
}
